package com.apestech.api.workflow.lib.app;

import com.alibaba.druid.pool.DruidDataSource;
import com.apestech.api.workflow.lib.FlowUtil;
import com.apestech.api.workflow.lib.element.FlowNodeInfo;
import com.apestech.api.workflow.lib.element.FlowTask;
import com.apestech.api.workflow.lib.element.WorkProcessEngine;
import com.apestech.framework.db.SqlResultSet;
import com.apestech.framework.db.SqlSession;
import com.apestech.framework.json.JSON;
import com.apestech.framework.json.JSONObject;
import com.apestech.framework.util.DBUtil;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.common.impl.interceptor.Command;
import org.flowable.engine.common.impl.interceptor.CommandContext;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.engine.runtime.Execution;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.service.impl.persistence.entity.HistoricTaskInstanceEntity;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import java.util.Date;

/**
 * 撤回、驳回、任意回退
 *
 * @author hup
 * @create 2018-07-27-11:57
 */
public class FlowJumpApp extends FlowUtil implements Command<Void> {

    private WorkProcessEngine engine;
    private JSONObject input;
    private JSONObject taskMap;
    private String processInstanceId;
    private String taskId;
    private String taskName;
    private String state;
    private String loginUserId;
    private String loginUserName;
    private TaskEntity curTaskEntity;  //正在执行
    private TaskEntity taskEntity; //要退回的结点
    private boolean refoulement;  //驱回
    private Process process;
    private FlowElement targetFlowElement;
    private String executeId;
    private String error;
    private HistoricTaskInstanceEntity hisTaskEntity;
    private FlowTask flowTask;
    private SqlSession session;

    public FlowJumpApp(WorkProcessEngine engine, JSONObject input, boolean refoulement) throws Exception {
        this.engine=engine;
        this.session= DBUtil.getSession(this.engine.getDataSource());
        this.session.setConnection(this.engine.getConnection());
        this.input=input;
        this.taskId=input.getString("taskId");
        if(this.taskId==null){
            throw new Exception("任务ID不允许为空！");
        }
        this.loginUserId=this.input.getString("loginUserId");
        if(this.loginUserId==null){
            throw new Exception("登录人员不允许为空！");
        }
        this.loginUserName=this.input.getString("loginUserName");
        this.refoulement=refoulement;
    }

    public String getError() {
        return error;
    }

    private void iniData(CommandContext commandContext) throws Exception {
        if(refoulement){
            doRefoulement(commandContext);
        } else {
            doRollBack(commandContext);
        }
        input.put("processInstanceId", flowTask.getProcessInstanceId());
        taskId=flowTask.getTask().getId();
        taskName=flowTask.getTask().getName();
        taskMap=getTaskMap(engine, taskId, input);
    }

    /**
     * 驱回，当前任务并没有执行，要求返回上一个任务结点
     */
    private void doRefoulement(CommandContext commandContext) throws Exception {
        curTaskEntity=CommandContextUtil.getTaskService(commandContext).getTask(taskId);
        if(curTaskEntity==null){
            throw new Exception("该任务不在运行状态，不允许做驳回操作！");
        }
        if(curTaskEntity.isSuspended()){
            throw new Exception("该任务为挂起状态，不允许做驳回操作！");
        }
        processInstanceId=curTaskEntity.getProcessInstanceId();
        process = ProcessDefinitionUtil.getProcess(curTaskEntity.getProcessDefinitionId());
        targetFlowElement = process.getFlowElement(curTaskEntity.getTaskDefinitionKey());
        FlowNodeInfo nodeInfo=new FlowNodeInfo(targetFlowElement, FlowNodeInfo.FlowOper.PRE);
        targetFlowElement=nodeInfo.getSourceFlow();
        if(targetFlowElement==null){
            throw new Exception("上一结点为空，不允许驳回！");
        }
        if(!nodeInfo.isSourceUserTask()){
            throw new Exception("上一结点不为USER类结点，不允许驳回！");
        }
        executeId=getExecuteId();
        HistoricTaskInstance hisTask=engine.getHistoryService().createHistoricTaskInstanceQuery()
                                            .processInstanceId(processInstanceId)
                                            .executionId(executeId)
                                            .taskDefinitionKey(targetFlowElement.getId()).list().get(0);
        hisTaskEntity=CommandContextUtil.getHistoricTaskService().getHistoricTask(hisTask.getId());
        if(hisTaskEntity==null){
            throw new Exception("上一结点的原始信息不存在，不允许驳回！");
        }
        taskEntity=getCompeteTask(commandContext, hisTaskEntity);
        flowTask= new FlowTask(engine, input);
        state=flowTask.getState();
        if("addSign".equals(state)){
            throw new Exception("当前运行结点，处于加签状态，不允许驳回！");
        }
        if("counterSign".equals(state) || "addSign-counterSign".equals(state)){
            throw new Exception("当前运行结点，处于会签状态，不允许驳回！");
        }
        if(hasExtendOper(flowTask.getTask().getId())){
            throw new Exception("当前运行结点做过加、转、会操作，不允许驳回！");
        }
    }

    /**
     * 任意结点回退，当前未执行结点－〉回退至传入结点
     * @param commandContext
     * @throws Exception
     */
    private void doRollBack(CommandContext commandContext) throws Exception {
        hisTaskEntity=CommandContextUtil.getHistoricTaskService().getHistoricTask(taskId);
        if(hisTaskEntity==null){
            throw new Exception("该结点的原始信息不存在，不允许回退！");
        }
        taskEntity=getCompeteTask(commandContext, hisTaskEntity);
        processInstanceId=taskEntity.getProcessInstanceId();
        process = ProcessDefinitionUtil.getProcess(taskEntity.getProcessDefinitionId());
        targetFlowElement = process.getFlowElement(taskEntity.getTaskDefinitionKey());

        input.remove("taskId");
        input.put("processInstanceId", processInstanceId);
        flowTask= new FlowTask(engine, input);
        if(flowTask.getTask()==null){
            throw new Exception("当前运行任务结点不存在，请检查数据！");
        }
        if(flowTask.getTask().isSuspended()){
            throw new Exception("当前运行任务为挂起状态，不允许做回退操作！");
        }
        if(taskId.equals(flowTask.getTask().getId())){
            throw new Exception("当前运行任务与正在运行任务为同一个，不需要做回退操作！");
        }
        state=flowTask.getState();
        if("addSign".equals(state)){
            throw new Exception("当前运行结点，处于加签状态，不允许回退！");
        }
        if("counterSign".equals(state) || "addSign-counterSign".equals(state)){
            throw new Exception("当前运行结点，处于会签状态，不允许回退！");
        }
        if(!flowTask.getProcessInstanceId().equals(taskEntity.getProcessInstanceId())){
            throw new Exception("当前运行结点为退回结点的子流程结点，不允许回退！");
        }
        if(hasExtendOper(flowTask.getTask().getId())){
            throw new Exception("当前运行结点做过加、转、会操作，不允许回退！");
        }
        curTaskEntity=CommandContextUtil.getTaskService(commandContext).getTask(flowTask.getTask().getId());
        if(!isRightFlowElement()){
            throw new Exception("当前运行结点步骤在要回退结点之前，该结点不允许回退！");
        }
    }

    private boolean isRightFlowElement() throws Exception {
        boolean flag=false;
        FlowElement curTaskFlow = process.getFlowElement(curTaskEntity.getTaskDefinitionKey());
        if(curTaskFlow.getId().equals(taskEntity.getTaskDefinitionKey())){
            return true;
        }
        FlowNodeInfo nodeInfo=new FlowNodeInfo(curTaskFlow, FlowNodeInfo.FlowOper.PRE);
        while(nodeInfo.getSourceFlow()!=null){
            if(nodeInfo.getSourceFlow().getId().equals(taskEntity.getTaskDefinitionKey())){
                flag=true;
                break;
            }
            nodeInfo=new FlowNodeInfo(nodeInfo.getSourceFlow(), FlowNodeInfo.FlowOper.PRE);
        }
        return flag;
    }

    private String getExecuteId(){
        Execution exec=engine.getRuntimeService().createExecutionQuery().parentId(processInstanceId).singleResult();
        if(exec!=null){
            return exec.getId();
        }
        return null;
    }

    private boolean hasExtendOper(String id) throws Exception {
        JSONObject m=new JSONObject();
        m.put("TASK_ID", id);
        return !session.getForResultSet().queryIsEmpty(" SELECT 1 FROM OA_TASKLIST WHERE 1=1 AND TASK_ID=#{TASK_ID} ", m);
    }

    @Override
    public Void execute(CommandContext commandContext) {
        error=null;
        try {
            iniData(commandContext);
            ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager();
            ExecutionEntity executionEntity = executionEntityManager.findById(curTaskEntity.getExecutionId());
            CommandContextUtil.getTaskService(commandContext).deleteTask(curTaskEntity, false);
            CommandContextUtil.getHistoricTaskService().deleteHistoricTask(
                    CommandContextUtil.getHistoricTaskService().createHistoricTask(curTaskEntity)
            );
            executionEntity.setCurrentFlowElement(targetFlowElement);
            CommandContextUtil.getExecutionEntityManager(commandContext).update(executionEntity);
            CommandContextUtil.getTaskService(commandContext).insertTask(taskEntity,false);
            CommandContextUtil.getHistoricTaskService().insertHistoricTask(CommandContextUtil.getHistoricTaskService().createHistoricTask(taskEntity), false);
            if(taskEntity.getAssignee()!=null){
                engine.getTaskService().claim(taskEntity.getId(), taskEntity.getAssignee());
            }
            input.put("userId", taskEntity.getAssignee());
            input.put("userName", taskEntity.getAssignee());
            System.out.println(JSON.toJSONString(taskMap));
            if(refoulement){
                writeTaskDb(engine, session, input, taskId, taskName, ExtendDbType.REFOULEMENT);
            } else {
                writeTaskDb(engine, session, input, taskId, taskName, ExtendDbType.ROLLBACK);
            }
        } catch (Exception e) {
            error=e.getMessage();
            if(error==null){
                error=e.toString();
            }
        }
        return null;
    }

    private TaskEntity getCompeteTask(CommandContext commandContext, HistoricTaskInstanceEntity hisTaskEntity){
        TaskEntity newTask=CommandContextUtil.getTaskService(commandContext).createTask();
        newTask.setExecutionId(hisTaskEntity.getExecutionId());
        newTask.setCreateTime(new Date());
        newTask.setProcessInstanceId(hisTaskEntity.getProcessInstanceId());
        //newTask.setId(hisTaskEntity.getId());
        newTask.setProcessDefinitionId(hisTaskEntity.getProcessDefinitionId());
        newTask.setScopeDefinitionId(hisTaskEntity.getScopeDefinitionId());
        newTask.setScopeId(hisTaskEntity.getScopeId());
        newTask.setScopeType(hisTaskEntity.getScopeType());
        newTask.setSubScopeId(hisTaskEntity.getSubScopeId());
        newTask.setTaskDefinitionKey(hisTaskEntity.getTaskDefinitionKey());
        newTask.setCategory(hisTaskEntity.getCategory());
        newTask.setAssignee(hisTaskEntity.getAssignee());
        newTask.setDescription(hisTaskEntity.getDescription());
        newTask.setName(hisTaskEntity.getName());
        newTask.setOwner(hisTaskEntity.getOwner());
        newTask.setFormKey(hisTaskEntity.getFormKey());
        newTask.setParentTaskId(hisTaskEntity.getParentTaskId());
        newTask.setPriority(hisTaskEntity.getPriority());
        newTask.setOriginalPersistentState(hisTaskEntity.getOriginalPersistentState());
        newTask.setRevision(hisTaskEntity.getRevision());
        newTask.setTenantId(hisTaskEntity.getTenantId());
        newTask.setVariableLocal(hisTaskEntity.getId(), hisTaskEntity.getTaskLocalVariables());
        newTask.setVariable(hisTaskEntity.getId(), hisTaskEntity.getProcessVariables());
        return newTask;
    }
}
